package com.beta.service.impl;

import com.baomidou.framework.service.impl.SuperServiceImpl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.beta.dto.order.OrderItemDto;
import com.beta.dto.order.RefundItemDto;
import com.beta.dto.product.ProductItemOptions;
import com.beta.dto.product.ProductPropertiesDto;
import com.beta.em.order.OrderItemStatus;
import com.beta.em.order.OrderStatus;
import com.beta.em.order.RefundStatus;
import com.beta.entity.OrderItems;
import com.beta.entity.Orders;
import com.beta.event.order.OrderItemEvent;
import com.beta.event.order.OrderItemPayloadEvent;
import com.beta.mapper.OrderItemsMapper;
import com.beta.service.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * OrderItems 表数据服务层接口实现类
 */
@Service
public class OrderItemsService extends SuperServiceImpl<OrderItemsMapper, OrderItems> implements IOrderItemsService {

    @Autowired
    private IProductPropertiesService productPropertiesService;
    @Autowired
    private IProductItemsService productItemsService;
    @Autowired
    private OrderItemsMapper orderItemsMapper;
    @Autowired
    private DateTimeService dbTimeService;
    @Autowired
    private IOrdersService orderService;
    @Autowired
    private PublishService publishService;
    @Autowired
    private IOrderOperateService orderOperateService;

    @Override
    public Map<Integer, List<OrderItemDto>> getOrdersItemsBatchByOrderIds(List<Integer> orderIds) {
        if (CollectionUtils.isEmpty(orderIds)) {
            return Collections.emptyMap();
        }

        List<OrderItemDto> orderItemDtos = orderItemsMapper.getOrdersItems(orderIds);
        for (OrderItemDto orderItemDto : orderItemDtos) {
            orderItemDto.setRejectable(getRejectable(orderItemDto));
        }
        List<Integer> productIds = orderItemDtos.stream().map(OrderItemDto::getProductId).collect(Collectors.toList());
        List<Integer> productItemIds = orderItemDtos.stream().map(OrderItemDto::getProductItemId).collect(Collectors.toList());
        Map<Integer, List<ProductPropertiesDto>> productPropertiesMap = productPropertiesService.getProductPropertiesBatch(productIds);
        Map<Integer, List<ProductItemOptions>> productItemOptionsMap = productItemsService.getProductItemOptionsBatch(productItemIds);
        for (OrderItemDto itemDto : orderItemDtos) {
            itemDto.setProperties(productPropertiesMap.get(itemDto.getProductId()));
            itemDto.setOptions(productItemOptionsMap.get(itemDto.getProductItemId()));
        }
        return orderItemDtos.stream().collect(Collectors.groupingBy(OrderItemDto::getOrderId));
    }

    @Override
    public List<OrderItemDto> getOrdersItemsByOrderId(Integer orderId) {
        List<OrderItemDto> ordersItems = baseMapper.getOrdersItems(Arrays.asList(orderId));
        for (OrderItemDto list : ordersItems) {
            list.setRejectable(getRejectable(list.getId()));
        }
        Map<Integer, List<OrderItemDto>> orderItemsMap = getOrdersItemsBatchByOrderIds(Arrays.asList(orderId));
        if (MapUtils.isEmpty(orderItemsMap)) {
            return Collections.emptyList();
        }
        return orderItemsMap.get(orderId);
    }

    @Override
    public List<OrderItemDto> getOrderItemByCartItemIds(List<Integer> cartItemIds) {
        return baseMapper.getOrderItemsByCartItemIds(cartItemIds);
    }

    @Override
    public void executeAgreeRefundItem(Integer refundId) {
        if(Objects.isNull(refundId)){
            throw new ServiceException("E40001");
        }
        String agreed = RefundStatus.AGREED.getValue();
        updateSelectiveById(new OrderItems().setRefundStatus(agreed).setId(refundId));

        OrderItems orderItems = selectById(refundId);
        // 发布订单项退款同意事件
        publishService.publishApplicationEvent(new OrderItemPayloadEvent(OrderItemEvent.REFUND_AGREE, orderItems));

        tryTriggerCompleteOrder(orderItems);

        updateSelectiveById(new OrderItems().setRefundStatus(agreed).setId(refundId));
    }

    @Override
    public Page<RefundItemDto> getRefundItems(Integer userId, String status, Page<RefundItemDto> page) {
        List<RefundItemDto> refundItemDtos = baseMapper.selectPageRefundItemsWithStatus(page, userId, status);
        for (RefundItemDto list : refundItemDtos) {
            list.setRejectable(getRejectable(list.getId()));
        }
        page.setRecords(refundItemDtos);
        return page;
    }

    @Override
    public Page<RefundItemDto> getAllRefundItems(Integer userId, Page<RefundItemDto> page) {
        if(Objects.isNull(userId)){
            throw new ServiceException("E00113");
        }
        List<RefundItemDto> refundItemDtos = baseMapper.selectPageRefundItems(page, userId);
        if(CollectionUtils.isEmpty(refundItemDtos)){
            return page;
        }
        for (RefundItemDto list : refundItemDtos) {
            list.setRejectable(getRejectable(list.getId()));
        }
        page.setRecords(refundItemDtos);
        return page;
    }

    @Override
    public String getOrderItemName(OrderItems orderItems) {
        if (Objects.isNull(orderItems)) {
            return "";
        }

        return new StringJoiner(",")
                .add(orderItems.getProductItemName())
                .add(orderItems.getProductItemText())
                .add(orderItems.getQuantity().toString())
                .toString();
    }

    @Override
    public List<OrderItems> getOrderItemsByOrderId(Integer orderId) {
        if (Objects.isNull(orderId)) {
            return Collections.emptyList();
        }

        return selectList(new EntityWrapper<>(new OrderItems().setOrderId(orderId)));
    }

    @Override
    public List<OrderItems> getRefundItems(Integer orderId) {
        List<OrderItems> orderItems = getOrderItemsByOrderId(orderId);
        return orderItems.stream().filter(orderItem ->
                OrderItemStatus.REJECTED.getValue().equals(orderItem.getStatus())
        ).collect(Collectors.toList());
    }

    @Override
    public List<OrderItems> getRefundAgreeItems(Integer orderId) {
        List<OrderItems> orderItems = getOrderItemsByOrderId(orderId);
        return orderItems.stream().filter(orderItem ->
                OrderItemStatus.REJECTED.getValue().equals(orderItem.getStatus())
                && RefundStatus.AGREED.getValue().equals(orderItem.getRefundStatus())
        ).collect(Collectors.toList());
    }

    @Override
    public List<OrderItems> getAcceptedItems(Integer orderId) {
        List<OrderItems> orderItems = getOrderItemsByOrderId(orderId);
        return orderItems.stream().filter(orderItem ->
                OrderItemStatus.ACCEPTED.getValue().equals(orderItem.getStatus())
        ).collect(Collectors.toList());
    }

    @Override
    public void executeReject(Integer orderItemId, BigDecimal refundRate, Integer currentUserId) {
        if(Objects.isNull(orderItemId)){
            throw new ServiceException("E40001");
        }
        if(Objects.isNull(currentUserId)){
            throw new ServiceException("E00113");
        }
        OrderItemDto orderItemDto = baseMapper.getOrderItem(orderItemId, currentUserId);
        orderItemDto.setRejectable(getRejectable(orderItemId));
        if (!orderItemDto.isRejectable()) {
            throw new ServiceException("E40011");
        }

        OrderItems updateEntity = new OrderItems().setId(orderItemId)
                .setStatus(OrderItemStatus.REJECTED.getValue())
                .setRefundRate(refundRate)
                .setRefundStatus(RefundStatus.PENDING.getValue())
                .setUpdatedAt(dbTimeService.toDbDate());
        updateSelectiveById(updateEntity);

        // 发布订单项退款事件
        OrderItems orderItems = selectById(orderItemId);
        publishService.publishApplicationEvent(new OrderItemPayloadEvent(OrderItemEvent.REFUND, orderItems));
    }

    @Override
    public void executeUnReject(Integer orderItemId, Integer currentUserId) {
        if (!getUnrejectable(orderItemId)) {
            return ;
        }

        OrderItems updateEntity = new OrderItems()
                .setId(orderItemId)
                .setStatus(OrderItemStatus.ACCEPTED.getValue())
                .setRefundRate(BigDecimal.ZERO)
                .setRefundStatus(RefundStatus.INIT.getValue())
                .setUpdatedAt(dbTimeService.toDbDate());
        updateSelectiveById(updateEntity);

        OrderItems orderItems = selectById(orderItemId);
        // 发布订单项退款取消事件
        publishService.publishApplicationEvent(new OrderItemPayloadEvent(OrderItemEvent.REFUND_CANCEL, orderItems));

        tryTriggerCompleteOrder(orderItems);
    }

    @Override
    public boolean getRejectable(Integer orderItemId) {
        if(Objects.isNull(orderItemId)){
            throw new ServiceException("E40001");
        }
        OrderItemDto ordersItem = baseMapper.getOrdersItemById(orderItemId);
        return getRejectable(ordersItem);
    }

    public boolean getRejectable(OrderItemDto orderItemDto) {
        RefundStatus refundStatus = RefundStatus.getRefundStatus(orderItemDto.getRefundStatus());
        String orderStatus = orderItemDto.getOrderStatus();
        String delivering = OrderStatus.DELIVERING.getValue();
        switch (refundStatus) {
            case INIT:
                return delivering.equals(orderStatus) ? true : false;
            case PENDING:
                String confirmed = OrderStatus.CONFIRMED.getValue();
                String rejected = OrderItemStatus.REJECTED.getValue();
                if (delivering.equals(orderStatus) || confirmed.equals(orderStatus) && rejected.equals(orderItemDto.getStatus()))
                    return true;
                else
                    return false;
            case AGREED:
                return false;
        }
        return false;
    }

    @Override
    public boolean getUnrejectable(Integer orderItemId) {
        if(Objects.isNull(orderItemId)){
            throw new ServiceException("E40001");
        }
        OrderItems orderItemEntity = selectById(orderItemId);
        String refundStatus = orderItemEntity.getRefundStatus();
        if (RefundStatus.INIT.getValue().equals(refundStatus)) {
            return false;
        } else if (RefundStatus.PENDING.getValue().equals(refundStatus))
            return true;
        else if (RefundStatus.AGREED.getValue().equals(refundStatus)) {
            return false;
        }
        return false;
    }

    private void tryTriggerCompleteOrder(OrderItems orderItems) {
        Orders orders = orderService.selectById(orderItems.getOrderId());
        if (Objects.isNull(orders)) {
            return ;
        }

        if (!OrderStatus.CONFIRMED.getValue().equals(orders.getStatus())) {
            return ;
        }

        // check 退款的订单项全部都已经同意
        List<OrderItems> refundItems = getRefundItems(orderItems.getOrderId());
        if (CollectionUtils.isNotEmpty(refundItems)) {
            boolean allAgreed = refundItems.stream().allMatch(item -> RefundStatus.AGREED.getValue().equals(item.getStatus()));
            if (!allAgreed) {
                return ;
            }
        }

        // 执行订单完成操作
        orderOperateService.executeCompleteOrder(orders.getId());
    }

}